package org.xbdframework.scheduling.quartz;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import org.apache.commons.lang3.StringUtils;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.quartz.QuartzJobBean;

/**
 * 默认定时任务QuartzJobBean
 *
 * @author luas
 * @since 4.3
 */
public abstract class XbdQuartzJobBean extends QuartzJobBean {

	protected final Logger logger = LoggerFactory.getLogger(getClass());

	public final static String DEFAULT_APPLICATIONCONTEXTSCHEDULERCONTEXTKEY = "applicationContextSchedulerContextKey";

	/**
	 * 目标任务类完整路径参数名称
	 */
	public final static String JOBDETAIL_KEY_TARGETCLASS = "targetClass";

	/**
	 * 目标任务类Spring Bean名称参数名称
	 */
	public final static String JOBDETAIL_KEY_TARGETOBJECT = "targetObject";

	/**
	 * 目标任务类待执行方法参数名称
	 */
	public final static String JOBDETAIL_KEY_TARGETMETHOD = "targetMethod";

	/**
	 * 目标任务类待执行方法参数参数名称 支持一般类型、复杂类型、数组类型、列表类型等
	 */
	public final static String JOBDETAIL_KEY_TARGETMETHOD_PARAM = "targetMethodParam";

	/**
	 * 目标任务类默认执行方法参数名称
	 *
	 * @see AbstractQuartzTask#DEFAULT_TARGETMETHOD
	 */
	public final static String JOBDETAIL_VALUE_TARGETMETHOD = AbstractQuartzTask.DEFAULT_TARGETMETHOD;

	/**
	 * 目标任务类完整路径
	 */
	private String targetClass;

	/**
	 * 目标任务类Spring Bean名称
	 */
	private String targetObject;

	/**
	 * 目标任务类待执行方法
	 */
	private String targetMethod;

	/**
	 * 目标任务类待执行方法参数 支持一般类型、复杂类型、数组类型、列表类型等
	 */
	private String targetMethodParam;

	protected void executeInternal(JobExecutionContext context, Object bean)
			throws JobExecutionException {
		try {
			if (StringUtils.isBlank(targetMethod)) {
				throw new JobExecutionException("定时任务目标方法为空！");
			}

			Method m;

			if (StringUtils.isBlank(targetMethodParam)) {
				m = bean.getClass().getMethod(targetMethod);

				m.invoke(bean, new Object[] {});
			}
			else {
				Object paramObject = JSON.parse(targetMethodParam);

				if (paramObject instanceof JSONObject) {
					m = bean.getClass().getMethod(targetMethod, Object.class);

					m.invoke(bean, new Object[] { targetMethodParam });
				}
				else if (paramObject instanceof JSONArray) {
					JSONArray params = (JSONArray) paramObject;

					List<Class> argClasss = new ArrayList<>();
					List<Object> args = new ArrayList<>();

					for (Object param : params) {
						argClasss.add(param.getClass());
						args.add(param);
					}

					m = bean.getClass().getMethod(targetMethod,
							argClasss.toArray(new Class[] {}));

					m.invoke(bean, ((JSONArray) paramObject).toArray(new Object[] {}));
				}
			}
		}
		catch (Exception ex) {
			throw new JobExecutionException(ex.getMessage(), ex);
		}
	}

	protected Object createJobInstance(ApplicationContext applicationContext)
			throws JobExecutionException {
		if (StringUtils.isBlank(this.targetClass)
				&& StringUtils.isBlank(this.targetObject)) {
			throw new JobExecutionException("定时任务目标对象为空！");
		}

		Object bean;

		try {
			if (StringUtils.isNotBlank(this.targetObject)) {
				bean = applicationContext.getBean(this.targetObject);
			}
			else {
				bean = Class.forName(this.targetClass).newInstance();
			}
		}
		catch (InstantiationException | IllegalAccessException
				| ClassNotFoundException e) {
			throw new JobExecutionException(e.getMessage(), e);
		}

		return bean;
	}

	public String getTargetClass() {
		return this.targetClass;
	}

	public void setTargetClass(String targetClass) {
		this.targetClass = targetClass;
	}

	public String getTargetObject() {
		return this.targetObject;
	}

	public void setTargetObject(String targetObject) {
		this.targetObject = targetObject;
	}

	public String getTargetMethod() {
		return this.targetMethod;
	}

	public void setTargetMethod(String targetMethod) {
		this.targetMethod = targetMethod;
	}

	public String getTargetMethodParam() {
		return this.targetMethodParam;
	}

	public void setTargetMethodParam(String targetMethodParam) {
		this.targetMethodParam = targetMethodParam;
	}

}
